﻿using System;
using System.Threading;
using log4net;

namespace Jiubang.Communication.Server.Buffers {
    /// <summary>
    /// </summary>
    public sealed class BufferManager {
        private readonly BufferPool[] _bufferPool;

        /// <summary>
        ///   缓冲区数量的上限
        /// </summary>
        private readonly int _capacity;

        private readonly ILog _log;
        private readonly int _mininum;
        private readonly int _padding;

        /// <summary>
        ///   缓冲区当前数量
        /// </summary>
        private int _currentCount;

        internal BufferManager() {}

        /// <summary>
        ///   初始化缓冲区。
        ///   最大可重用缓冲区容量计算公式为：mininum + padding * (count-1)。
        /// </summary>
        /// <param name = "mininum">
        ///   缓冲区容量的下限。
        ///   管理器不会创建小于该容量的缓冲区。
        /// </param>
        /// <param name = "count">
        ///   缓冲区容量值域的数量。
        /// </param>
        /// <param name = "padding">
        ///   缓冲区增长的步长。
        /// </param>
        /// <param name = "capacity">
        ///   缓冲区数量的上限
        /// </param>
        public BufferManager(int mininum, int count, int padding, int capacity) {
            if (mininum <= 0)
                throw new ArgumentOutOfRangeException("mininum");

            _mininum = mininum;
            _padding = padding;
            _capacity = capacity;

            _log = LogManager.GetLogger(typeof (IndentedBuffer));

            _bufferPool = new BufferPool[count];
            for (var i = 0; i < _bufferPool.Length; i++)
                _bufferPool[i] = new BufferPool(mininum + padding*i);
        }

        /// <summary>
        ///   检查请求的缓冲区大小是否超过最大可管理缓冲区上限。
        ///   如是则创建独立临时缓冲区，
        ///   否则尝试从池中返回一个可用的缓冲空间。
        ///   如池已空则按池的容量创建一个新的缓冲区。
        /// </summary>
        /// <param name = "applySize">申请缓冲区的最小长度</param>
        /// <returns></returns>
        public byte[] Apply(int applySize) {
            var index = Math.Max((int) Math.Ceiling((double) (applySize - _mininum)/_padding), 0);
            var pool = index >= _bufferPool.Length
                           ? null
                           : _bufferPool[index];

            byte[] buffer;
            if (pool == null) {
                if (_log.IsWarnEnabled)
                    _log.Warn("申请的空间" + applySize + "超过缓冲区最大容量，创建独立缓冲区");
                return new byte[applySize];
            }
            if (pool.TryPop(out buffer)) {
                Interlocked.Decrement(ref _currentCount);
                return buffer;
            }
            return pool.Create();
        }

        /// <summary>
        ///   判断缓冲区是否属于正常的容量范围。
        ///   对象可加入缓冲池时检查缓冲区总数是否达到上限。
        ///   如是则从饥饿值最小的池中移除足够的项。
        ///   最后将缓冲区加入池中。
        /// </summary>
        /// <param name = "buffer"></param>
        /// <returns></returns>
        public bool Return(byte[] buffer) {
            if ((buffer.Length - _mininum)%_padding != 0)
                return false;

            var index = (buffer.Length - _mininum)/_padding;
            if (index >= _bufferPool.Length)
                return false;

            _bufferPool[index].Push(buffer);

            BufferPool pool;
            while (_currentCount >= _capacity) {
                pool = _bufferPool[0];
                for (var i = 1; i < _bufferPool.Length; i++) {
                    if (_bufferPool[i].Starvation < pool.Starvation)
                        pool = _bufferPool[i];
                }

                if (pool.Remove()) {
                    var size = Interlocked.Decrement(ref _currentCount);
                    if (_log.IsInfoEnabled)
                        _log.Info("回收[" + pool.Length + "]，当前池大小为[" + pool.Count + "/" + size + "]");
                }
            }

            return true;
        }
    }
}